#ifndef __RECORD_SET_H__
  #define __RECORD_SET_H__

#include <ostream>
#include <string>
#include <iomanip>

#include "array2d.h"
#include "char_instance_manager.h"

#define CHAR_INSTANCE_MANAGER_SIZE (16 * 1024 - 1) // in byte(s)

using namespace std;

//! An implementation for holding the data derived from database

/*!
A record_set is an object representation of data derived by a query from database (for example sql select).
This class will be used by database classes as well.

Implementation date: 2007

Usage:

\code
smart_pointer<database> db = new postgre_database();
record_set rs;

try {
  db->connect("127.0.0.1", "postgres", "passme", "postgres", "protocol_file.txt");

  db->execute_query("SELECT * FROM \"T1\" LIMIT 10;", rs);
  cout << rs << endl;
} catch (const general_exception &ge) {
  cerr << ge.get_info() << endl;
  cerr << ge.get_message() << endl;
}
\endcode
*/

class record_set {
friend ostream &operator<<(ostream &os, const record_set &rs);

private:
  array2d<const char*> data; // the first row contains the column names
  array2d<unsigned int> data_length; // the length of each item, first row contains the column names
  char_memory_management::char_instance_manager cim;

  record_set &operator=(const record_set &rs); // not allowed
  record_set(const record_set &rs); // not allowed

public:

  //! fills the record_set with the given value and its length
  /*!
  this can only be called by a sub-class of database (for example postgre_database).
  reserve(..) or reconstruct(..) must be called before calling this method to allocate
  enough rows & columns.
  this method is suitable for sequential calls, since every call fills the next
  value in the recordset in a stream manner.
  */
  void fill_record_set(const char *value, unsigned int length);

  //! fills the record_set at index(i, j) with the given value and its length
  /*!
  this can only be called by a sub-class of database (for example postgre_database).
  reserve(..) or reconstruct(..) must be called before calling this method to allocate
  enough rows & columns.
  this method can be used by multiple threads simultaneously as far as they use different (i, j) indexes.
  row index of 0 means the column-names of the table & not the first record.
  */
  void fill_record_set(unsigned int row, unsigned int column, const char *value, unsigned int length);

  //! resizes the record_set and keeps its content intact (may throw an exception)
  void reserve(unsigned int rows, unsigned int columns);

  //! reconstructs the record_set and deletes its content, cim will also be released (may throw an exception)
  void reconstruct(unsigned int total_rows, unsigned int total_columns);

  //! reduces the size of the record_set (may throw an exception)
  void cut_rows_after(unsigned int index);

  //! returns the number of rows
  unsigned int get_row_count() const;

  //! returns the number of columns
  unsigned int get_column_count() const;

  //! returns the column-name of the given index (may throw an exception)
  const char *get_column_name(unsigned int index) const;

  //! returns the column-index of the given name, or UINT_MAX if not found (may throw an exception)
  unsigned int get_column_index(const char *column_name) const;

  //! returns the length of the column-name of the given index (may throw an exception)
  unsigned int get_column_length(unsigned int index) const;

  //! returns the data length of the given index, which is specially useful for binary objects (may throw an exception)
  unsigned int get_length(unsigned int row, unsigned int column) const;

  //! index operator using column index (may throw an index_exception)
  const char *operator()(unsigned int row, unsigned int column) const;

  //! index operator using column name (may throw an index_exception)
  const char *operator()(unsigned int row, const char *column_name) const;

  /**
  comparison_array is a 2d-array that holds boolean values, meaning if the field
  at index (i, j) is changed or not.
  returns true if both record_set objects are equal
  */
  bool compare(const record_set &rs, array2d<bool> &comparison_array, bool check_column_names = true) const;

  //! default constructor
  record_set();

  //! destructor (non-virtual: please do not inherit from this class)
  ~record_set();
}; // class record_set

#endif
